package com.wzg.security;


import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Date;

import javax.crypto.Cipher;

/**
 * @author henryGui
 * @version V1.0
 * @Title: HQCertificate
 * @Package com.wzg.security
 * @Description: TODO
 * @date 2018-07-09 下午3:33
 */
public class HQCertificate {


    private static HQCertificate certificate = new HQCertificate();

    public static HQCertificate getInstance()
    {
        return certificate;
    }

    private HQCertificate()
    {
    }

    /**
     * 密钥库
     *
     * @author jianggujin
     *
     */
    public static enum HQKeyStore
    {

        JCEKS("jceks"), JKS("jks"), DKS("dks"), PKCS11("pkcs11"), PKCS12("pkcs12");
        private String name;

        private HQKeyStore(String name)
        {
            this.name = name;
        }

        public String getName()
        {
            return this.name;
        }
    }

    /**
     * Java密钥库(Java Key Store，JKS)KEY_STORE
     */
    // public final String KEY_STORE = "JKS";

    public final String X509 = "X.509";

    /**
     * 由KeyStore获得私钥
     *
     * @param keyStorePath
     * @param alias
     * @param password
     * @return
     * @throws Exception
     */
    private PrivateKey getPrivateKey(String keyStorePath, String alias, char[] password, HQKeyStore keyStore)
            throws Exception
    {
        KeyStore ks = getKeyStore(keyStorePath, password, keyStore);
        PrivateKey key = (PrivateKey) ks.getKey(alias, password);
        return key;
    }

    /**
     * 由Certificate获得公钥
     *
     * @param certificatePath
     * @return
     * @throws Exception
     */
    private PublicKey getPublicKey(String certificatePath) throws Exception
    {
        Certificate certificate = getCertificate(certificatePath);
        PublicKey key = certificate.getPublicKey();
        return key;
    }

    /**
     * 获得Certificate
     *
     * @param certificatePath
     * @return
     * @throws Exception
     */
    private Certificate getCertificate(String certificatePath) throws Exception
    {
        CertificateFactory certificateFactory = CertificateFactory.getInstance(X509);
        FileInputStream in = new FileInputStream(certificatePath);
        Certificate certificate = certificateFactory.generateCertificate(in);
        in.close();
        return certificate;
    }

    /**
     * 获得Certificate
     *
     * @param keyStorePath
     * @param alias
     * @param password
     * @return
     * @throws Exception
     */
    private Certificate getCertificate(String keyStorePath, String alias, char[] password, HQKeyStore keyStore)
            throws Exception
    {
        KeyStore ks = getKeyStore(keyStorePath, password, keyStore);
        return getCertificate(ks, alias);
    }

    private Certificate getCertificate(KeyStore keyStore, String alias) throws Exception
    {
        Certificate certificate = keyStore.getCertificate(alias);
        return certificate;
    }

    /**
     * 获得KeyStore
     *
     * @param keyStorePath
     * @param password
     * @return
     * @throws Exception
     */
    public KeyStore getKeyStore(String keyStorePath, char[] password, HQKeyStore keyStore) throws Exception
    {
        KeyStore store = null;
        FileInputStream is = new FileInputStream(keyStorePath);
        store = getKeyStore(is, password, keyStore);
        is.close();
        return store;
    }

    public KeyStore getKeyStore(InputStream in, char[] password, HQKeyStore keyStore) throws Exception
    {
        KeyStore ks = KeyStore.getInstance(keyStore.getName());
        ks.load(in, password);
        return ks;
    }

    /**
     * 私钥加密
     *
     * @param data
     * @param keyStorePath
     * @param alias
     * @param password
     * @return
     * @throws Exception
     */
    public byte[] encrypt(byte[] data, String keyStorePath, String alias, char[] password, HQKeyStore keyStore)
            throws Exception
    {
        // 取得私钥
        PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password, keyStore);

        return encrypt(data, privateKey);

    }

    public byte[] encrypt(byte[] data, PrivateKey privateKey) throws Exception
    {
        // 对数据加密
        Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        return cipher.doFinal(data);
    }

    /**
     * 公钥加密
     *
     * @param data
     * @param certificatePath
     * @return
     * @throws Exception
     */
    public byte[] encrypt(byte[] data, String certificatePath) throws Exception
    {
        // 取得公钥
        PublicKey publicKey = getPublicKey(certificatePath);
        return encrypt(data, publicKey);

    }

    public byte[] encrypt(byte[] data, PublicKey publicKey) throws Exception
    {
        // 对数据加密
        Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        return cipher.doFinal(data);

    }

    /**
     * 私钥解密
     *
     * @param data
     * @param keyStorePath
     * @param alias
     * @param password
     * @return
     * @throws Exception
     */
    public byte[] decrypt(byte[] data, String keyStorePath, String alias, char[] password, HQKeyStore keyStore)
            throws Exception
    {
        // 取得私钥
        PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password, keyStore);
        return decrypt(data, privateKey);
    }

    public byte[] decrypt(byte[] data, PrivateKey privateKey) throws Exception
    {
        // 对数据加密
        Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        return cipher.doFinal(data);
    }

    /**
     * 公钥解密
     *
     * @param data
     * @param certificatePath
     * @return
     * @throws Exception
     */
    public byte[] decrypt(byte[] data, String certificatePath) throws Exception
    {
        // 取得公钥
        PublicKey publicKey = getPublicKey(certificatePath);
        // 对数据加密
        Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        return decrypt(data, publicKey);
    }

    public byte[] decrypt(byte[] data, PublicKey publicKey) throws Exception
    {
        // 对数据加密
        Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        return cipher.doFinal(data);

    }

    /**
     * 验证Certificate
     *
     * @param certificatePath
     * @return
     */
    public boolean verifyCertificate(String certificatePath)
    {
        return verifyCertificate(new Date(), certificatePath);
    }

    /**
     * 验证Certificate是否过期或无效
     *
     * @param date
     * @param certificatePath
     * @return
     */
    public boolean verifyCertificate(Date date, String certificatePath)
    {
        boolean status = true;
        try
        {
            // 取得证书
            Certificate certificate = getCertificate(certificatePath);
            // 验证证书是否过期或无效
            status = verifyCertificate(date, certificate);
        }
        catch (Exception e)
        {
            status = false;
        }
        return status;
    }

    /**
     * 验证证书是否过期或无效
     *
     * @param date
     * @param certificate
     * @return
     */
    private boolean verifyCertificate(Date date, Certificate certificate)
    {
        boolean status = true;
        try
        {
            X509Certificate x509Certificate = (X509Certificate) certificate;
            x509Certificate.checkValidity(date);
        }
        catch (Exception e)
        {
            status = false;
        }
        return status;
    }

    /**
     * 签名
     *
     * @param keyStorePath
     * @param alias
     * @param password
     *
     * @return
     * @throws Exception
     */
    public byte[] sign(byte[] data, String keyStorePath, String alias, char[] password, HQKeyStore keyStore)
            throws Exception
    {
        // 获得证书
        Certificate certificate = getCertificate(keyStorePath, alias, password, keyStore);
        // 取得私钥
        PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password, keyStore);
        return sign(data, certificate, privateKey);
    }

    public byte[] sign(byte[] data, Certificate certificate, PrivateKey privateKey) throws Exception
    {
        // 获得证书
        X509Certificate x509Certificate = (X509Certificate) certificate;

        // 构建签名
        Signature signature = Signature.getInstance(x509Certificate.getSigAlgName());
        signature.initSign(privateKey);
        signature.update(data);
        return signature.sign();
    }

    /**
     * 验证签名
     *
     * @param data
     * @param sign
     * @param certificatePath
     * @return
     * @throws Exception
     */
    public boolean verify(byte[] data, byte[] sign, String certificatePath) throws Exception
    {
        // 获得证书
        Certificate certificate = getCertificate(certificatePath);

        return verify(data, sign, certificate);
    }

    public boolean verify(byte[] data, byte[] sign, Certificate certificate) throws Exception
    {
        // 获得证书
        X509Certificate x509Certificate = (X509Certificate) certificate;
        // 获得公钥
        PublicKey publicKey = x509Certificate.getPublicKey();
        // 构建签名
        Signature signature = Signature.getInstance(x509Certificate.getSigAlgName());
        signature.initVerify(publicKey);
        signature.update(data);

        return signature.verify(sign);
    }

    /**
     * 验证Certificate
     *
     * @param keyStorePath
     * @param alias
     * @param password
     * @return
     */
    public boolean verifyCertificate(Date date, String keyStorePath, String alias, char[] password, HQKeyStore keyStore)
    {
        boolean status = true;
        try
        {
            Certificate certificate = getCertificate(keyStorePath, alias, password, keyStore);
            status = verifyCertificate(date, certificate);
        }
        catch (Exception e)
        {
            status = false;
        }
        return status;
    }

    /**
     * 验证Certificate
     *
     * @param keyStorePath
     * @param alias
     * @param password
     * @return
     */
    public boolean verifyCertificate(String keyStorePath, String alias, char[] password, HQKeyStore keyStore)
    {
        return verifyCertificate(new Date(), keyStorePath, alias, password, keyStore);
    }
}
